/*
 *  Copyright 2005 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.persistence.hibernate.forum;

import com.blandware.atleap.common.util.ConvertUtil;
import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.ContentField;
import com.blandware.atleap.model.core.ContentFieldValue;
import com.blandware.atleap.model.core.ContentLocale;
import com.blandware.atleap.model.core.User;
import com.blandware.atleap.model.forum.Post;
import com.blandware.atleap.model.forum.Topic;
import com.blandware.atleap.persistence.forum.PostDAO;
import com.blandware.atleap.persistence.hibernate.core.PageDAOHibernate;

import java.util.*;

/**
 * <p>Hibernate implementation of PostDAO.</p>
 * <p><a href="PostDAOHibernate.java.html"><i>View source</i></a></p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.9 $ $Date: 2008/06/23 13:24:54 $
 */
public class PostDAOHibernate extends PageDAOHibernate implements PostDAO {

    // ~ CRUD Methods ================================================================

    /**
     * @see com.blandware.atleap.persistence.forum.PostDAO#createPost(com.blandware.atleap.model.forum.Post, com.blandware.atleap.model.forum.Topic)
     */
    public Long createPost(Post post, Topic topic) {
        post.setDate(new Date());
        post.setUsageCounter(new Integer(0));
        post.setLastUpdatedDatetime(new Date());
        post.setActive(Boolean.TRUE);
        topic.addPost(post);

        User user = post.getAuthorUser();
        if (user != null) {
            post.setAuthorUser(user);
            post.setAuthorStr(user.getFullName());
            post.setEmailStr(user.getEmail());
        }

        Long postId = (Long) getHibernateTemplate().save(post);

        ContentLocale language = topic.getForum().getLanguage();

		// title
		ContentField fTitle = new ContentField();
		fTitle.setIdentifier("title");
		fTitle.setType(ContentField.LINE_TYPE);
		fTitle.setInternal(Boolean.TRUE);
		post.addContentField(fTitle);
		getHibernateTemplate().save(fTitle);

		// body
		ContentField fBody = new ContentField();
		fBody.setIdentifier("body");
		fBody.setType(ContentField.HTML_TYPE);
		fBody.setInternal(Boolean.TRUE);
		post.addContentField(fBody);
		getHibernateTemplate().save(fBody);

        // values for title and body
        String valueTitle = post.getSubject();
        String valueBody = post.getBody();

        // title
        ContentFieldValue fvTitle = new ContentFieldValue();
        fvTitle.setContentLocale(language);
        if ( valueTitle != null ) {
            fvTitle.setSimpleValue(valueTitle);
        } else {
            fvTitle.setSimpleValue("");
        }
        fvTitle.setLastUpdatedDatetime(new Date());
        fTitle.addContentFieldValue(fvTitle);
        getHibernateTemplate().save(fvTitle);

        // body
        ContentFieldValue fvBody = new ContentFieldValue();
        fvBody.setContentLocale(language);

        if ( valueBody != null ) {
            byte[] annotationBytes = ConvertUtil.convertToByteArray(valueBody);
            fvBody.setValue(annotationBytes);
            // get linked objects
        } else {
            fvBody.setValue(new byte[0]);
        }

        fvBody.setLastUpdatedDatetime(new Date());
        fBody.addContentFieldValue(fvBody);
        getHibernateTemplate().save(fvBody);

        return postId;
    }

    /**
     * @see com.blandware.atleap.persistence.forum.PostDAO#retrievePost(Long)
     */
    public Post retrievePost(Long postId) {
        List args = new ArrayList();
        String hql = new StringBuffer("select post, title.simpleValue, body.value from Post post ")
                .append("left outer join post.contentFields as titleField ")
                .append("left outer join titleField.contentFieldValues as title ")
                .append("left outer join post.contentFields as bodyField ")
                .append("left outer join bodyField.contentFieldValues as body ")
                .append("where titleField.identifier = 'title' ")
                .append("and bodyField.identifier = 'body' ")
                .append("and title.contentLocale.identifier = post.topic.forum.language.identifier ")
                .append("and body.contentLocale.identifier = post.topic.forum.language.identifier ")
                .append("and post.id = ? ")
                .toString();
        args.add(postId);
        Object[] objects = (Object[]) findUniqueResult(hql, args.toArray());

        Post post = null;

        if (objects != null) {
            Map title = new HashMap();
            post = (Post) objects[0];
            title.put(post.getTopic().getForum().getLanguage().getIdentifier(), objects[1]);
            post.setTitle(title);
            post.setBody(ConvertUtil.convertToString((byte[]) objects[2]));
        }

        return post;
    }

    /**
     * @see com.blandware.atleap.persistence.forum.PostDAO#updatePost(com.blandware.atleap.model.forum.Post, com.blandware.atleap.model.forum.Topic)
     */
    public void updatePost(Post post, Topic topic) {
		Post oldPost = topic.updatePost(post);
        removeFromCache(oldPost);

		getHibernateTemplate().update(post);
    }

    /**
     * @see com.blandware.atleap.persistence.forum.PostDAO#updatePost(com.blandware.atleap.model.forum.Post)
     */
    public void updatePost(Post post) {
		Post oldPost = post.getTopic().updatePost(post);
        removeFromCache(oldPost);

		getHibernateTemplate().update(post);
    }

    /**
     * @see com.blandware.atleap.persistence.forum.PostDAO#deletePost(com.blandware.atleap.model.forum.Post)
     */
    public void deletePost(Post post) {
        removeAssociations(post);

        getHibernateTemplate().delete(post);
        post.getTopic().removePost(post);
    }

    // ~ Additional methods ================================================================

    /**
     * @see com.blandware.atleap.persistence.forum.PostDAO#listPosts(com.blandware.atleap.common.util.QueryInfo)
     */
    public PartialCollection listPosts(QueryInfo queryInfo) {
		String whereClause = "";
		String orderByClause = "";
		if ( queryInfo != null ) {
			whereClause = queryInfo.getWhereClause();
			orderByClause = queryInfo.getOrderByClause();
		}

		if ( whereClause == null || whereClause.length() == 0 ) {
			whereClause = "";
		}
		if ( orderByClause != null && orderByClause.length() != 0 ) {
			orderByClause = " order by " + orderByClause;
		} else {
			orderByClause = " order by post.date ";
		}

        Long topicId = null;
        if ( queryInfo != null ) {
            if ( queryInfo.getQueryParameters() != null ) {
                topicId = (Long) queryInfo.getQueryParameters().get("topicId");
            }
        }

		List list;
		Integer total = null;

		String hqlPart;
		ArrayList args = new ArrayList();
        hqlPart = new StringBuffer("from Post as post ")
                .append("left outer join post.contentFields as titleField ")
                .append("left outer join titleField.contentFieldValues as title ")
                .append("left outer join post.contentFields as bodyField ")
                .append("left outer join bodyField.contentFieldValues as body ")
                .append("where titleField.identifier = 'title' ")
                .append("and bodyField.identifier = 'body' ")
                .append("and title.contentLocale.identifier = post.topic.forum.language.identifier ")
                .append("and body.contentLocale.identifier = post.topic.forum.language.identifier ")
                .toString();
        if (topicId != null) {
            hqlPart += "and post.topic.id = ? ";
            args.add(topicId);
        }
        if (whereClause.length() > 0) {
            hqlPart = hqlPart + "and " + whereClause;
        }

		if ( queryInfo != null && queryInfo.getLimit() != null && queryInfo.getOffset() != null ) {
			// query count
			String hqlForTotal = "select count(distinct post.id) " + hqlPart;
			total = findUniqueIntegerResult(hqlForTotal, args.toArray());
			if ( total == null ) {
				total = new Integer(0);
			}
		}

        // If we don't have any info about the total number of results yet or
        // we know that there's something that will be found, then fetch data
		if ( total == null || total.intValue() > 0 ) {
			String hql;
    		hql = "select post, title.simpleValue, body.value " + hqlPart + orderByClause;
			list = executeFind(hql, queryInfo, args.toArray());
            Set ids = new HashSet();
            List posts = new ArrayList();
            if (list != null) {
                for ( ListIterator i = list.listIterator(); i.hasNext(); ) {
                    Object[] objects = (Object[]) i.next();
                    Map title = new HashMap();
                    Post post = (Post) objects[0];
                    Long postId = post.getId();
                    if (!ids.contains(postId)) {
                        title.put(post.getTopic().getForum().getLanguage().getIdentifier(), objects[1]);
                        post.setTitle(title);
                        post.setBody(ConvertUtil.convertToString((byte[]) objects[2]));

                        posts.add(post);
                        ids.add(postId);
                    }
                }
            }
            list = posts;
            if ( total == null ) {
                total = new Integer(list.size());
            }
        } else {
			list = new ArrayList();
		}

		return new PartialCollection(list, total);
    }
}
